//-----------------------------------------------------------------------------
//
//Copyright(c) 2020, ThorsianWay Technologies Co, Ltd
//All rights reserved.
//
//IP Name       :   gpu_bus_matrix_core
//File Name     :   gpu_bus_matrix_core.v
//Module name   :   gpu_bus_matrix_core
//Full name     :   OpenGpu2020 gpu bus matrix top
//
//Author        :   xiang tian
//Email         :   
//Data          :   2020/5/5
//Version       :   V1.0
//
//Abstract      :   
//                  
//Called  by    :   GPU
//
//Modification history
//-----------------------------------------------------
//1.00: intial version 
//
//
//-----------------------------------------------------------------------------

//-----------------------------
//DEFINE MACRO
//-----------------------------      

`include "gpu_bus.sv"
module gpu_bus_matrix_core #
(
	parameter PORT_NUM =4,
    parameter PORT_WIDTH = 2,
    parameter ADDR_WIDTH = 32,
    parameter DATA_WIDTH = 128,
    parameter ID_WIDTH = 4,
    parameter BL_WIDTH = 8,
    parameter CMD_DEPTH = 2
)
(
	input           clk,
	input           rst_n,
    gpu_bus  sink[PORT_NUM],
    gpu_bus  source
);




//****************************************
//  command channel
//****************************************

wire  [PORT_NUM-1:0]  chnl_req;
wire  [PORT_NUM-1:0]  chnl_ack;
wire [PORT_WIDTH-1:0] chnl_sel;
wire  [PORT_NUM-1:0]  chnl_ack_latch;


wire  [PORT_NUM*ADDR_WIDTH-1:0]   inter_Gaddr;
wire  [PORT_NUM*BL_WIDTH-1:0]     inter_Gbl;
wire  [PORT_NUM*ID_WIDTH-1:0]     inter_Gid;
wire  [PORT_NUM-1:0]              inter_Gwrite;

wire  arbiter_en;


genvar i;
generate

for(i=0;i<PORT_NUM;i=i+1)
begin: Sink_Addr_interface

    sink_addr_store #
    (
        .ADDR_WIDTH(ADDR_WIDTH),
        .ID_WIDTH  (ID_WIDTH),
        .BL_WIDTH  (BL_WIDTH),
        .ADDR_DEPTH(CMD_DEPTH)
    ) u_sink_addr_store
    (
        .clk(clk),
        .rst_n(rst_n),
        .Gaddr  (sink[i].Gaddr),
        .Gbl    (sink[i].Gbl),
        .Gid    (sink[i].Gid),
        .Gwrite (sink[i].Gwrite),
        .Gvalid (sink[i].Gvalid),
        .Gready (sink[i].Gready),
        .SGaddr (inter_Gaddr[ADDR_WIDTH*i+:ADDR_WIDTH]),
        .SGbl   (inter_Gbl[BL_WIDTH*i+:BL_WIDTH]),                    
        .SGid   (inter_Gid[ID_WIDTH*i+:ID_WIDTH]),       
        .SGwrite(inter_Gwrite[i]),                      
        .req    (chnl_req[i]),
        .ack    (chnl_ack[i])
    );    


end

endgenerate


gpu_bus_arbiter #
(
    .PORT_NUM(PORT_NUM),
    .PORT_WIDTH(PORT_WIDTH)
) u_gpu_bus_arbiter
(
    .clk  (clk),
    .rst_n(rst_n),
    .en   (arbiter_en),
    .req  (chnl_req),
    .gnt  (chnl_ack),
    .sel  (),
    .gnt_latch(),
    .sel_latch(chnl_sel)
);       


assign source.Gaddr  = source.Gvalid ? inter_Gaddr[ADDR_WIDTH*chnl_sel+:ADDR_WIDTH] : 0;
assign source.Gbl    = source.Gvalid ? inter_Gbl[BL_WIDTH*chnl_sel+:BL_WIDTH] : 0;
assign source.Gid    = source.Gvalid ? {chnl_sel,inter_Gid[ID_WIDTH*chnl_sel+:ID_WIDTH]} : 0;
assign source.Gwrite = source.Gvalid ? inter_Gwrite[chnl_sel] : 0;
assign source.Gvalid = arbiter_en;


//***********************************************
// write data path
//***********************************************

logic [PORT_NUM*DATA_WIDTH-1:0] inter_Gwdata;
logic [PORT_NUM-1:0]            inter_Gwvalid;
logic [PORT_NUM-1:0]            inter_Gwfirst;
logic [PORT_NUM-1:0]            inter_Gwlast;
logic [PORT_NUM-1:0]            inter_Gwready;

genvar ii;

generate
for(ii=0;ii<PORT_NUM;ii=ii+1)
begin
    assign    inter_Gwdata[DATA_WIDTH*ii+:DATA_WIDTH] = sink[ii].Gwdata;
    assign    inter_Gwvalid[ii] = sink[ii].Gwvalid;
    assign    inter_Gwfirst[ii] = sink[ii].Gwfirst;
    assign    inter_Gwlast[ii]  = sink[ii].Gwlast;
    assign    sink[ii].Gwready  = inter_Gwready[ii];
end
endgenerate

assign source.Gwdata  = inter_Gwdata[DATA_WIDTH*chnl_sel+:DATA_WIDTH];
assign source.Gwvalid = inter_Gwvalid[chnl_sel];
assign source.Gwfirst = inter_Gwfirst[chnl_sel];
assign source.Gwlast  = inter_Gwlast[chnl_sel];


reg  writing;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n) writing <= 1'b0;
    else if(arbiter_en && source.Gwrite) writing <= 1'b1;
    else if(source.Gwlast && source.Gwready) writing <= 1'b0;
end



always @*
begin
    inter_Gwready = 0;
    if(writing) inter_Gwready[chnl_sel] = source.Gwready;
    else inter_Gwready = 0;
end




//***********************************************
// read data path
//***********************************************

wire [PORT_WIDTH-1:0]  read_chnl = source.Grid[PORT_WIDTH+ID_WIDTH-1:ID_WIDTH];

logic [PORT_NUM*DATA_WIDTH-1:0]  inter_Grdata;
logic [PORT_NUM*ID_WIDTH-1:0]    inter_Grid;
logic [PORT_NUM-1:0]             inter_Grvalid;
logic [PORT_NUM-1:0]             inter_Grfirst;
logic [PORT_NUM-1:0]             inter_Grlast;
logic [PORT_NUM-1:0]             inter_Grready;

genvar kk;

generate
for(kk=0;kk<PORT_NUM;kk=kk+1)
begin
    assign    sink[kk].Grdata   = inter_Grdata[DATA_WIDTH*kk+:DATA_WIDTH];
    assign    sink[kk].Grid     = inter_Grid[ID_WIDTH*kk+:ID_WIDTH];
    assign    sink[kk].Grvalid  = inter_Grvalid[kk];
    assign    sink[kk].Grfirst  = inter_Grfirst[kk];
    assign    sink[kk].Grlast   = inter_Grlast[kk];
    assign    inter_Grready[kk] = sink[kk].Grready;
end
endgenerate

integer k;

always @*
begin
    source.Grready = 0;
    for(k=0;k<PORT_NUM;k=k+1)
        if(k==read_chnl)
        begin
            inter_Grdata[DATA_WIDTH*k+:DATA_WIDTH]  = source.Grdata;
            inter_Grid[ID_WIDTH*k+:ID_WIDTH]        = source.Grid[ID_WIDTH-1:0];
            inter_Grvalid[k] = source.Grvalid;
            inter_Grfirst[k] = source.Grfirst;
            inter_Grlast[k]  = source.Grlast;
            
            source.Grready = inter_Grready[k]; 
        end
        else begin
            inter_Grdata[DATA_WIDTH*k+:DATA_WIDTH]  = 0;
            inter_Grid[ID_WIDTH*k+:ID_WIDTH]    = 0;
            inter_Grvalid[k] = 0;
            inter_Grfirst[k] = 0;
            inter_Grlast[k]  = 0;
        end
end



//********************************************
// arbiter enable/ command issue
//********************************************



assign arbiter_en = source.Gready && (|chnl_req) && !writing;

	
endmodule
